#include "csv.h"
#include <fstream>
#include <string>
#include <algorithm>

namespace anet {
	namespace utils {
		// static global string 
		std::string CCSVParser::m_staticStr("");

		CCSVParser::CCSVParser() {
			m_vecLines.reserve(1000);
		}

		CCSVParser::~CCSVParser() = default;

		bool CCSVParser::Load(const char* path, bool has_field_head) {
			if (!path) {
				return false;
			}

			try {
				std::ifstream ifs(path, std::ios::in | std::ios::binary);
				if (!ifs) {
					return false;
				}
				std::string line;
				line.reserve(1024);

				int lineNum = 0;
				while (std::getline(ifs, line)) {
					if (line.empty()) {
						continue;
					}
					parseLine(line, lineNum++ == 0);
				}

				if (has_field_head && !m_vecLines.empty()) {
					const auto& header = m_vecLines[0];
					for (size_t indx = 0; indx < header.size(); indx++) {
						if (m_field2index.find(header[indx]) != m_field2index.end()) {
							return false;
						}
						m_field2index[header[indx]] = indx;
					}
				}
				return true;
			} catch (const std::exception& e) {
				(void)e;
				return false;
			}
		}

		void CCSVParser::parseCSVLine(const std::string& line, std::vector<std::string>& fields) {
			fields.clear();
			fields.reserve(32);

			std::string field;
			field.reserve(256);
			bool inQuotes = false;
			bool escaped = false;

			for (char c : line) {
				if (escaped) {
					field += c;
					escaped = false;
					continue;
				}

				if (!inQuotes) {
					if (c == ',') {
						fields.push_back(std::move(field));
						field.clear();
					} else if (c == '"') {
						inQuotes = true;
					} else if (c == '\\') {
						escaped = true;
					} else {
						field += c;
					}
				} else {
					if (c == '"') {
						if (field.empty() || field.back() != '"') {
							inQuotes = false;
						} else {
							field.pop_back();
							field += '"';
						}
					} else if (c == '\\') {
						escaped = true;
					} else {
						field += c;
					}
				}
			}

			if (!field.empty()) {
				fields.push_back(std::move(field));
			}
		}

		void CCSVParser::parseLine(std::string& strLine, bool first) {
			// Try to remove BOM
			if (first && strLine.size() >= 3) {
				const unsigned char* dst = reinterpret_cast<const unsigned char*>(strLine.c_str());
				if (dst[0] == 0xEF && dst[1] == 0xBB && dst[2] == 0xBF) {
					strLine.erase(0, 3);
				}
			}

			strLine = TrimSpace(strLine);
			if (strLine.empty()) {
				return;
			}

			std::vector<std::string> fields;
			parseCSVLine(strLine, fields);

			// Check any empty fields.
			if (std::any_of(fields.begin(), fields.end(), [](const std::string& f) { return !f.empty(); })) {
				m_vecLines.push_back(std::move(fields));
			}
		}

		size_t CCSVParser::GetLineCount() const {
			return m_vecLines.size();
		}

		size_t CCSVParser::GetFieldCount(size_t line) const {
			if (line >= GetLineCount()) {
				return 0;
			}
			return m_vecLines[line].size();
		}

		const std::string& CCSVParser::GetField(size_t line, size_t field) const {
			if (line >= GetLineCount() || field >= m_vecLines[line].size()) {
				return m_staticStr;
			}
			return m_vecLines[line][field];
		}

		const std::string& CCSVParser::GetField(size_t line, const char* field) const {
			auto it = m_field2index.find(field);
			if (it == m_field2index.end()) {
				return m_staticStr;
			}
			return GetField(line, it->second);
		}

		const std::string& CCSVParser::GetFieldWithoutError(size_t line, const char* field) const {
			return GetField(line, field);
		}

		const std::string& CCSVParser::GetField(size_t line, size_t field) {
			if (line >= GetLineCount()) {
				return m_staticStr;
			}

			const CFieldsVec& vecFields = m_vecLines[line];
			if (field >= vecFields.size()) {
				return m_staticStr;
			}
			return vecFields[field];
		}

		std::string CCSVParser::GetLine(size_t line) const {
			if (line >= GetLineCount()) {
				return "";
			}

			std::string result;
			result.reserve(1024);

			const auto& fields = m_vecLines[line];
			for (size_t i = 0; i < fields.size(); i++) {
				if (i > 0) {
					result += ",";
				}

				const auto& field = fields[i];
				if (field.find(',') != std::string::npos) {
					result += "\"";
					result += field;
					result += "\"";
				} else {
					result += field;
				}
			}

			return result;
		}

		void CCSVParser::Write(const char* pszCsvFile) {
			if (!pszCsvFile) {
				return;
			}

			std::ofstream of(pszCsvFile, std::ios::out | std::ios::binary);
			if (!of) {
				return;
			}

			for (const auto& line : m_vecLines) {
				of << GetLine(&line - &m_vecLines[0]) << "\n";
			}
			of.flush();
		}

		bool CCSVParser::HasLine(size_t line) const {
			return line < m_vecLines.size();
		}

		bool CCSVParser::HasField(size_t line, size_t field) const {
			return HasLine(line) && field < m_vecLines[line].size();
		}

		bool CCSVParser::HasField(size_t line, const char* field) {
			return HasLine(line) && m_field2index.find(field) != m_field2index.end();
		}

		long long CCSVParser::GetFieldInt64(size_t line, const char* field) const {
			return toUserType<long long>(GetField(line, field));
		}

		int CCSVParser::GetFieldInt(size_t line, const char* field) const {
			return toUserType<int>(GetField(line, field));
		}

		unsigned int CCSVParser::GetFieldUint(size_t line, const char* field) const {
			return toUserType<unsigned int>(GetField(line, field));
		}

		double CCSVParser::GetFiledFloat(size_t line, const char* field) const {
			return toUserType<double>(GetField(line, field));
		}

		bool CCSVParser::GetFieldBool(size_t line, const char* field) const {
			return toUserType<bool>(GetField(line, field));
		}
	}
}